home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / nntpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-23  |  20.4 KB  |  762 lines

  1. /*
  2.  *    Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *    Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *
  8.  *    Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *    Permission granted for non-commercial copying and use, provided
  10.  *    this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  */
  32. #include <stdio.h>
  33. #include <sys/types.h>
  34. #include <time.h>
  35. #include <sys/timeb.h>
  36. #include <ctype.h>
  37. #include <string.h>  /* for strchr() */
  38. #ifdef    __TURBOC__
  39. #include <dir.h>
  40. #endif
  41. #include "global.h"
  42. #include "config.h"
  43. #include "timer.h"
  44. #include "cmdparse.h"
  45. #include "commands.h"
  46. #include "socket.h"
  47. #include "usock.h"
  48. #include "netuser.h"
  49. #include "proc.h"
  50. #include "smtp.h"
  51. #include "mailutil.h"
  52. #include "files.h"
  53.  
  54. #ifdef    NNTP
  55. #define NNTPMAXLEN    512
  56.  
  57. struct nntpservers {
  58.     struct timer nntpcli_t;
  59.     char *name;
  60.     char *groups;
  61.     int lowtime, hightime;  /* for connect window */
  62.     struct nntpservers *next;
  63. };
  64.  
  65. #define    NULLNNTP    (struct nntpservers *)NULL
  66.  
  67. #define MAXGROUPDIRS 10
  68.  
  69. static struct grouploc {
  70.     char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  71.     char *directory;     /* directory where these groups should be */
  72.     } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  73.  
  74. static struct nntpservers *Nntpservers = NULLNNTP;
  75. static char *Nntpgroups = NULLCHAR;
  76. static unsigned short nntptrace = 1;
  77. static int nntpquiet = 0;
  78. static char *News_spool = NULL;
  79. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  80.  
  81. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  82.  
  83. static void nntptick __ARGS((void *tp));
  84. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  85. static int gettxt __ARGS((int s,FILE *fp));
  86. static int getreply __ARGS((int s));
  87. static int getarticle __ARGS((int s,char *msgid));
  88. static int dogroups __ARGS((int argc,char *argv[],void *p));
  89. static int doadds __ARGS((int argc,char *argv[],void *p));
  90. static int dodrops __ARGS((int argc,char *argv[],void *p));
  91. static int dokicks __ARGS((int argc,char *argv[],void *p));
  92. static int dolists __ARGS((int argc,char *argv[],void *p));
  93. static int donntrace __ARGS((int argc,char *argv[],void *p));
  94. static int donnquiet __ARGS((int argc,char *argv[],void *p));
  95. static int dondir __ARGS((int argc,char *argv[],void *p));
  96. extern int subcmd __ARGS((struct cmds tab[],int argc,char *argv[],void *p));
  97.  
  98. /* Tracing levels:
  99.     0 - no tracing
  100.     1 - serious errors reported
  101.     2 - transient errors reported
  102.     3 - session progress reported
  103.     4 - actual received articles displayed
  104.  */
  105.  
  106. static struct cmds DFAR Nntpcmds[] = {
  107.     "addserver",    doadds,    0,    3,
  108.     "nntp addserver <nntpserver> <interval>",
  109.     "directory",    dondir,    0,    0,    NULLCHAR,
  110.     "dropserver",    dodrops,    0,    2,
  111.     "nntp dropserver <nntpserver>",
  112.     "groups",    dogroups,    0,    0,    NULLCHAR,
  113.     "kick",        dokicks,    0,    2,
  114.     "nntp kick <nntpserver>",
  115.     "listservers",    dolists,    0,    0,    NULLCHAR,
  116.     "quiet",    donnquiet,    0,    0,    NULLCHAR,
  117.     "trace",    donntrace,    0,    0,    NULLCHAR,
  118.     NULLCHAR,
  119. };
  120.  
  121. int
  122. donntp(argc,argv,p)
  123. int argc;
  124. char *argv[];
  125. void *p;
  126. {
  127.     return subcmd(Nntpcmds,argc,argv,p);
  128. }
  129.  
  130. static int
  131. doadds(argc,argv,p)
  132. int argc;
  133. char *argv[];
  134. void *p;
  135. {
  136.     struct nntpservers *np;
  137.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  138.         if(stricmp(np->name,argv[1]) == 0)
  139.             break;
  140.     if (np == NULLNNTP) {
  141.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  142.         np->name = strdup(argv[1]);
  143.         np->next = Nntpservers;
  144.         Nntpservers = np;
  145.         np->groups = NULLCHAR;
  146.         np->lowtime = np->hightime = -1;
  147.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  148.         np->nntpcli_t.arg = (void *)np;
  149.     }
  150.     if (argc > 3) {
  151.         int i;
  152.         if (np->groups == NULLCHAR) {
  153.             np->groups = mallocw(NNTPMAXLEN);
  154.             *np->groups = '\0';
  155.         }
  156.         for (i = 3; i < argc; ++i) {
  157.             if (isdigit(*argv[i])) {
  158.                 int lh, ll, hh, hl;
  159.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  160.                 np->lowtime = lh * 100 + ll;
  161.                 np->hightime = hh * 100 + hl;
  162.             } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  163.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  164.             else {  /* it's a group, and it fits... add it to list */
  165.                 if (*np->groups != '\0')
  166.                     strcat(np->groups, ",");
  167.                 strcat(np->groups, argv[i]);
  168.             }
  169.         }
  170.         if (*np->groups == '\0') {    /* No groups specified? */
  171.             free(np->groups);
  172.             np->groups = NULLCHAR;
  173.         }
  174.     }
  175.     /* set timer duration */
  176.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  177.     start_timer(&np->nntpcli_t);        /* and fire it up */
  178.     return 0;
  179. }
  180.  
  181. static int
  182. dodrops(argc,argv,p)
  183. int argc;
  184. char *argv[];
  185. void *p;
  186. {
  187.     struct nntpservers *np, *npprev = NULLNNTP;
  188.     for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  189.         if(stricmp(np->name,argv[1]) == 0) {
  190.             stop_timer(&np->nntpcli_t);
  191.             free(np->name);
  192.             if (np->groups)
  193.                 free(np->groups);
  194.             if(npprev != NULLNNTP)
  195.                 npprev->next = np->next;
  196.             else
  197.                 Nntpservers = np->next;
  198.             free((char *)np);
  199.             return 0;
  200.     }
  201.     tprintf("No such server enabled.\n");
  202.     return 0;
  203. }
  204.  
  205. static int
  206. dolists(argc,argv,p)
  207. int argc;
  208. char *argv[];
  209. void *p;
  210. {
  211.     struct nntpservers *np;
  212.     for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  213.         char tbuf[80];
  214.         if (np->lowtime != -1 && np->hightime != -1)
  215.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  216.         else
  217.             tbuf[0] = '\0';
  218.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  219.             read_timer(&np->nntpcli_t) /1000L,
  220.             dur_timer(&np->nntpcli_t) /1000L,
  221.             tbuf, np->groups ? np->groups : "");
  222.     }
  223.     return 0;
  224. }
  225.  
  226. static int donntrace(argc, argv, p)
  227. int argc;
  228. char *argv[];
  229. void *p;
  230. {
  231.     return setshort(&nntptrace,"NNTP tracing",argc,argv);
  232. }
  233.     
  234. static int donnquiet(argc, argv, p)
  235. int argc;
  236. char *argv[];
  237. void *p;
  238. {
  239.     return setbool(&nntpquiet,"NNTP quiet",argc,argv);
  240. }
  241.     
  242. static int dondir(argc, argv, p)
  243. int argc;
  244. char *argv[];
  245. void *p;
  246. {
  247.     if (argc < 2) {
  248.         int i;
  249.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  250.         tprintf("control: %s\n", Newsdir);
  251.         for (i = 0; i < MAXGROUPDIRS; ++i)
  252.             if (groupdirs[i].prefix)
  253.                 tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  254.     } else {
  255.         char *p;
  256.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  257.             int i;
  258.             *p++ = '\0';
  259.             for (i = 0; i < MAXGROUPDIRS; ++i)
  260.                 if (groupdirs[i].prefix)
  261.                     if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  262.                         if (groupdirs[i].directory) {
  263.                             free(groupdirs[i].directory);
  264.                             groupdirs[i].directory = NULLCHAR;
  265.                             }
  266.                         if (*p == '\0') {
  267.                             free(groupdirs[i].prefix);
  268.                             groupdirs[i].prefix = NULLCHAR;
  269.                         } else
  270.                             groupdirs[i].directory = strdup(p);
  271.                         return 0;
  272.                     }
  273.             if (*p == '\0')  /* trashing a group that's not there */
  274.                 return 0;
  275.             for (i = 0; i < MAXGROUPDIRS; ++i){
  276.                 if (groupdirs[i].prefix == NULLCHAR) {
  277.                     groupdirs[i].prefix = strdup(argv[1]);
  278.                     if (groupdirs[i].directory) {
  279.                         free(groupdirs[i].directory);
  280.                         groupdirs[i].directory = NULL;
  281.                     }
  282.                     groupdirs[i].directory = strdup(p);
  283.                     return 0;
  284.                 }
  285.             }
  286.             tprintf("Directory table full\n");
  287.         }
  288.         else {  /* no '=', so just set default */
  289.             if (News_spool)
  290.                 free(News_spool);
  291.             News_spool = strdup(argv[1]);
  292.         }
  293.         if (argc > 2) {  /* they specified a newsdir as well */
  294.             if (np_all)
  295.                 free(Newsdir);
  296.             Newsdir = strdup(argv[2]);
  297.             np_all = 1;
  298.         }
  299.     }
  300.     return 0;
  301. }
  302.     
  303. static int
  304. dokicks(argc,argv,p)
  305. int argc;
  306. char *argv[];
  307. void *p;
  308. {
  309.     struct nntpservers *np;
  310.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  311.         if(stricmp(np->name,argv[1]) == 0) {
  312.             /* If the timer is not running, the timeout function has
  313.             * already been called and we don't want to call it again.
  314.             */
  315.             if(run_timer(&np->nntpcli_t)) {
  316.                 stop_timer(&np->nntpcli_t);
  317.             }
  318.             nntptick((void *)np);
  319.             return 0;
  320.     }
  321.     tprintf("No such server enabled.\n");
  322.     return 1;
  323. }
  324.  
  325. static int
  326. dogroups(argc,argv,p)
  327. int argc;
  328. char *argv[];
  329. void *p;
  330. {
  331.     int i;
  332.     if(argc < 2) {
  333.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  334.             tprintf("All groups are currently enabled.\n");
  335.         else
  336.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  337.         return 0;
  338.     }
  339.     if(Nntpgroups == NULLCHAR)
  340.         Nntpgroups = mallocw(NNTPMAXLEN);
  341.     *Nntpgroups = '\0';
  342.     for(i=1; i < argc; ++i) {
  343.         if(i > 1)
  344.             strcat(Nntpgroups,",");
  345.         strcat(Nntpgroups,argv[i]);
  346.     }
  347.     return 0;
  348. }
  349.  
  350. /* This is the routine that gets called every so often to connect to
  351.  * NNTP servers.
  352.  */
  353. static void
  354. nntptick(tp)
  355. void *tp;
  356. {
  357.     newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0);
  358. }
  359.  
  360. static void
  361. nntp_job(i1,tp,v1)
  362. int i1;
  363. void *tp, *v1;
  364. {
  365.     FILE *fp, *tmpf;
  366.     int s = -1, i;
  367. /*    long pos; */
  368.     struct tm *ltm;
  369.     time_t t;
  370.     int now;
  371.     struct nntpservers *np = (struct nntpservers *) tp;
  372.     struct sockaddr_in fsocket;
  373.     char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
  374.     if (nntptrace >= 3)
  375.         tprintf("NNTP daemon entered, target = %s\n",np->name);
  376.     if(availmem() < Memthresh + 4000L){
  377.         if (nntptrace >= 2)
  378.             tprintf("NNTP daemon quit -- low memory\n");
  379.         /* Memory is tight, don't do anything */
  380.         start_timer(&np->nntpcli_t);
  381.         return;
  382.     }
  383.  
  384.     time(&t);    /* more portable than gettime() */
  385.     ltm = localtime(&t);
  386.     now = ltm->tm_hour * 100 + ltm->tm_min;
  387.     if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  388.         if (now < np->lowtime || now >= np->hightime) {
  389.             if (nntptrace >= 3)
  390.                 tprintf("NNTP window to '%s' not open\n", np->name);
  391.             start_timer(&np->nntpcli_t);
  392.             return;
  393.         }
  394.     } else {
  395.         if (now < np->lowtime && now >= np->hightime) {
  396.             if (nntptrace >= 3)
  397.                 tprintf("NNTP window to '%s' not open\n", np->name);
  398.             start_timer(&np->nntpcli_t);
  399.             return;
  400.         }
  401.     }
  402.  
  403.     fsocket.sin_addr.s_addr = resolve(np->name);
  404.     if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  405.         if (nntptrace >= 2)
  406.             tprintf("NNTP can't resolve host '%s'\n", np->name);
  407.         /* Try again later */
  408.         start_timer(&np->nntpcli_t);
  409.         return;
  410.     }
  411.     fsocket.sin_family = AF_INET;
  412.     fsocket.sin_port = IPPORT_NNTP;
  413.  
  414.     s = socket(AF_INET,SOCK_STREAM,0);
  415.     sockmode(s,SOCK_ASCII);
  416.     if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  417.         cp = sockerr(s);
  418.         log(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
  419.             cp != NULLCHAR ? cp : "");
  420.         if (nntptrace >= 2)
  421.             tprintf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
  422.         cp != NULLCHAR ? cp : "");
  423.         goto quit;
  424.     }
  425.     /* Eat the banner */
  426.     i = getreply(s);
  427.     if(i == -1 || i >= 400) {
  428.         log(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
  429.         if (nntptrace >= 1)
  430.             tprintf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
  431.         goto quit;
  432.     }
  433.  
  434.     if (mlock(Newsdir, "nntp")) {
  435.         if (nntptrace >= 2)
  436.             tprintf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
  437.         goto quit;
  438.     }
  439.     sprintf(buf,"%s/nntp.dat",Newsdir);
  440.     if((fp = fopen(buf,UPDATE_TEXT)) == NULLFILE) {
  441.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
  442.             buf);
  443.         if (nntptrace >= 1)
  444.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  445.         rmlock(Newsdir, "nntp");
  446.         goto quit;
  447.     }
  448.     rewind(fp);
  449. /*    for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  450.     for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  451.         if((cp = strchr(buf,' ')) == NULLCHAR)
  452.             continue;    /* something wrong with this line, skip it */
  453.         *cp = '\0';
  454.         if(stricmp(buf,np->name) == 0) {
  455.             rip(cp+1);
  456.             lastdate = strdup(cp+1);
  457.             break;
  458.         }
  459.     }
  460.     fclose(fp);
  461.     rmlock(Newsdir, "nntp");
  462.  
  463.     if(lastdate == NULLCHAR)
  464.         lastdate = strdup("700101 000000");
  465.     /* snapshot the time for use later in re-writing nntp.dat */
  466.     time(&t);
  467.     ltm = localtime(&t);
  468.                 
  469.     /* Get a list of new message-id's */
  470.     if (np->groups) {
  471.         if (nntptrace >= 3)
  472.             tprintf("==>NEWNEWS %s %s\n", np->groups, lastdate);
  473.         usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
  474.     } else {
  475.         if (nntptrace >= 3)
  476.             tprintf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  477.         usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  478.     }
  479.     free(lastdate);
  480.     /* Get the response */
  481.     if((i = getreply(s)) != 230) { /* protocol error */
  482.         log(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
  483.         if (nntptrace >= 1)
  484.             tprintf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
  485.         goto quit;
  486.     }
  487.     if((tmpf = tmpfile()) == NULLFILE) {
  488.         if (nntptrace >= 1)
  489.             tprintf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
  490.         goto quit;
  491.     }
  492.     if(gettxt(s,tmpf) == -1) {
  493.         log(s, "NNTP %s giving up: gettxt() failure",psocket(&fsocket));
  494.         if (nntptrace >= 1)
  495.             tprintf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
  496.         fclose(tmpf);
  497.         goto quit;
  498.     }
  499.  
  500.     /* Open the history file */
  501.     if (mlock(Newsdir, "history")) {
  502.         if (nntptrace >= 1)
  503.             tprintf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
  504.         fclose(tmpf);
  505.         goto quit;
  506.     }
  507.     sprintf(buf,"%s/history",Newsdir);
  508.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  509.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
  510.         if (nntptrace >= 1)
  511.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  512.         fclose(tmpf);
  513.         goto quit;
  514.     }
  515.     /* search through the history file for matching message id's */
  516.     rewind(tmpf);
  517.     while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  518.         i = 0;
  519.         rewind(fp);
  520.         while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  521.             if(stricmp(buf,tbuf) == 0) {
  522.                 i = 1;
  523.                 break;
  524.             }
  525.             pwait(NULL);
  526.         }
  527.         if(i == 0) {        /* not found, get the article */
  528.             if(getarticle(s,tbuf) == -1) {
  529.                 log(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
  530.                 if (nntptrace >= 2)
  531.                     tprintf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
  532.                 fclose(fp);
  533.                 rmlock(Newsdir, "history");
  534.                 fclose(tmpf);
  535.                 goto quit;
  536.             }
  537.             fprintf(fp,"%s",tbuf); /* add the new message id */
  538.         }
  539.     }
  540.     fclose(fp);
  541.     rmlock(Newsdir, "history");
  542.     fclose(tmpf);
  543.     if (nntptrace >= 3)
  544.         tprintf("==>QUIT\n");
  545.     usprintf(s,"QUIT\n");
  546.     /* Eat the response */
  547.     getreply(s);
  548.     /* NOW, update the nntp.dat file */
  549.     if (mlock(Newsdir, "nntp")) {
  550.         if (nntptrace >= 2)
  551.             tprintf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
  552.         goto quit;
  553.     }
  554.     sprintf(buf,"%s/nntp.dat",Newsdir);
  555.     fp = fopen(buf,READ_TEXT);
  556.     sprintf(buf, "%s/nntp.tmp",Newsdir);
  557.     if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  558.         if (nntptrace >= 1)
  559.             tprintf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
  560.     if (fp == NULLFILE || tmpf == NULLFILE) {
  561.         log(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
  562.         if (nntptrace >= 2)
  563.             tprintf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
  564.         if (fp)
  565.             fclose(fp);
  566.         if (tmpf)
  567.             fclose(tmpf);
  568.         rmlock(Newsdir, "nntp");
  569.         goto quit;
  570.     }
  571.     while (fgets(tbuf, sizeof(tbuf), fp))
  572.         if (strnicmp(tbuf, np->name, strlen(np->name)))
  573.             fputs(tbuf, tmpf);
  574.     fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
  575.         ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  576.     fclose(fp);
  577.     fclose(tmpf);
  578.     sprintf(buf, "%s/nntp.dat", Newsdir);
  579.     sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  580.     unlink(buf);
  581.     rename(tbuf, buf);
  582.     rmlock(Newsdir, "nntp");
  583. quit:
  584.     if (nntptrace >= 3)
  585.         tprintf("NNTP daemon exiting\n");
  586.     close_s(s);
  587.     /* Restart timer */
  588.     start_timer(&np->nntpcli_t);
  589.     return;
  590. }
  591.  
  592. static int
  593. gettxt(s,fp)
  594. int s;
  595. FILE *fp;
  596. {
  597.     char buf[NNTPMAXLEN];
  598.     int nlines;
  599.     for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  600.         if (nntptrace >= 4)
  601.             tprintf("<==%s", buf);
  602.         if(strcmp(buf,".\n") == 0) {
  603.             if (nntptrace >= 3)
  604.                 tprintf("NNTP received %d lines\n", nlines);
  605.             return 0;
  606.             }
  607.         /* check for escaped '.' characters */
  608.         if(strcmp(buf,"..\n") == 0)
  609.             fputs(".\n",fp);
  610.         else
  611.             fputs(buf,fp);
  612.     }
  613.     if (nntptrace >= 1)
  614.         tprintf("NNTP receive error after %d lines\n", nlines);
  615.     return -1;
  616. }
  617.  
  618. static int
  619. getreply(s)
  620. int s;
  621. {
  622.     char buf[NNTPMAXLEN];
  623.     int response;
  624.     while(recvline(s,buf,NNTPMAXLEN) != -1) {
  625.         /* skip informative messages and blank lines */
  626.         if(buf[0] == '\0' || buf[0] == '1')
  627.             continue;
  628.         sscanf(buf,"%d",&response);
  629.         if (nntptrace >= 3)
  630.             tprintf("<==%s\n", buf);
  631.         return response;
  632.     }
  633.     if (nntptrace >= 3)
  634.         tprintf("==No response\n");
  635.     return -1;
  636. }
  637.  
  638. static int
  639. getarticle(s,msgid)
  640. int s;
  641. char *msgid;
  642. {
  643.     char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  644.     FILE *fp, *tmpf;
  645.     int r;
  646.     char *cp;
  647.     extern int Smtpquiet;
  648. #ifdef USERLOG
  649.     time_t t;
  650. #endif
  651.  
  652.     if (nntptrace >= 3)
  653.         tprintf("==>ARTICLE %s", msgid);
  654.     usprintf(s,"ARTICLE %s", msgid);
  655.     r = getreply(s);
  656.     if(r == -1 || r >= 500)
  657.         return -1;
  658.     if(r >= 400)
  659.         return 0;
  660.     if((tmpf = tmpfile()) == NULLFILE) {
  661.         if (nntptrace >= 1)
  662.             tprintf("NNTP Cannot open temp file for article\n");
  663.         return -1;
  664.     }
  665.     if(gettxt(s,tmpf) == -1) {
  666.         fclose(tmpf);
  667.         return -1;
  668.     }
  669.     /* convert the article into mail format */
  670.     rewind(tmpf);
  671.     froml[0] = '\0';
  672.     newgl[0] = '\0';
  673.     while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  674.         if(strncmp(buf,"From: ",6) == 0) {
  675.             struct timeb t;
  676.             ftime(&t);
  677.             rip(&buf[6]);
  678.             sprintf(froml,"From %s %ld\n",&buf[6], t.time);
  679.             if(newgl[0] != '\0')
  680.                 break;
  681.         }
  682.         if(strncmp(buf,"Newsgroups: ",12) == 0) {
  683.             strcpy(newgl,&buf[12]);
  684.             if(froml[0] != '\0')
  685.                 break;
  686.         }
  687.         /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  688.         if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  689. /*            fclose(fp); */
  690.             fclose(tmpf);
  691.             return 0;
  692.         }
  693.     }
  694.     sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  695.     for(cp=newgl;;++cp) {
  696.         if(*cp == '.') {
  697. #ifdef __TURBOC__
  698.             mkdir(buf); /* create a subdirectory, if necessary */
  699. #else
  700.             mkdir(buf,0755); /* create a subdirectory, if necessary */
  701. #endif
  702.             strcat(buf,"/");
  703.             continue;
  704.         }
  705.         if(*cp == ',' || *cp == '\n') {
  706.             char tempdir[80], prefix[20], *p;
  707.             strcpy(tempdir, buf);
  708.             if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  709.                 *p++ = '\0';
  710.                 strcpy(prefix, p);
  711.             }
  712.             if (mlock(tempdir, prefix)) {
  713.                 if (nntptrace >= 2)
  714.                     tprintf("NNTP group '%s' is locked\n", buf);
  715.                 return -1;
  716.             }
  717.             strcat(buf,".txt");
  718.             /* open the mail file */
  719.             if (nntptrace >= 3)
  720.                 tprintf("Writing article to '%s'\n", buf);
  721.             if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  722.                 fputs(froml,fp);
  723. #ifdef USERLOG
  724.                 /* If the userlog code is enabled, we need a
  725.                  * "Received: " line to get the message id
  726.                  * that is used in it - WG7J
  727.                  */
  728.                 time(&t);
  729.                 fprintf(fp,Hdrs[RECEIVED]);
  730.                 fprintf(fp,"by %s with NNTP\n\tid AA%ld ; %s",
  731.                         Hostname, get_msgid(0),ptime(&t));
  732. #endif
  733.                 rewind(tmpf);
  734.                 while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  735.                     /* for UNIX mail compatiblity */
  736.                     if(strncmp(buf,"From ",5) == 0)
  737.                         putc('>',fp);
  738.                     fputs(buf,fp);
  739.                 }
  740.                 putc('\n',fp);
  741.                 fclose(fp);
  742.             }
  743.             rmlock(tempdir, prefix);
  744.             if (*cp == '\n') 
  745.                 break;
  746.             else
  747.                 sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  748.             continue;
  749.         }
  750.         buf[strlen(buf)+1] = '\0';
  751.         buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  752.     }
  753.     fclose(tmpf);
  754.     strcpy(buf,msgid);        /* Get a copy we can munge */
  755.     rip(buf);            /* remove trailing new-line */
  756.     rip(newgl);            /* ditto */
  757.     if(!nntpquiet)
  758.         tprintf("New news arrived: %s, article %s%c\n",newgl,buf,Smtpquiet?' ':'\007');
  759.     return 0;
  760. }
  761. #endif    /* NNTP */
  762.